/* 
 * File:   Nave.cpp
 * Author: cesar
 *
 *  Implementacion de la clase Objetivo.hpp y sus metodos
 */

#include "Objetivo.hpp"
#define RADIO 5.0 //radio de colision
#define ZSPAWN -900 //distancia de aparicion de las naves
#define ZKILL 100 // distancia maxima de cercania de las naves
#define EXPLOSION_LIMIT 35//cantidad de frames que debe durar la explosion

/**
 * 
 * @param i : El identificador del objetivo(para el picking).
 * @param objs : Apuntador a los demas objetivos.
 * @param n : Apuntador a la nave.
 * @param nombre : Nombre del modelo a utilizar.
 * @param w : Ancho de la pantalla.
 * @param h : Alto de la pantalla.
 */
Objetivo::Objetivo(int i, Objetivo** objs, Nave * n, char * nombre, int w, int h) : Modelo(0.7, nombre, 0.0, 0.0, 100.0) {
    id = i;
    objetivos = objs;
    nave = n;
    ancho_pantalla = abs(w - 1);
    alto_pantalla = abs(h - 1);
    randomPos();
    luz_explosion = GL_LIGHT4;
    explosion_counter = 0;
    recovered = false;
    selection_mode = false;


    //reflexion ambiental del material
    material_ambient[0] = 0.1;
    material_ambient[1] = 0.1;
    material_ambient[2] = 0.1;

    //reflexion difusa del material
    material_difuso[0] = 0.2;
    material_difuso[1] = 0.4;
    material_difuso[2] = 0.6;


    //reflexion especular del material
    material_especular[0] = 0.774597;
    material_especular[1] = 0.774597;
    material_especular[2] = 0.774597;

    material_especular[0] = 1.0;
    material_especular[1] = 1.0;
    material_especular[2] = 1.0;


    //emision de luz roja
    material_emision[0] = 0.1;
    material_emision[1] = 0.1;
    material_emision[2] = 0.1;

    //brillo
    material_brillo[0] = 128.0;

    //cargar sonido de explosion
    cargarSonido("Explode.wav", false);

}

/**
 * Metodo que permite dibujar un objetivo en la escena, en su estado normal 
 * o destruido.
 */
void Objetivo::dibujar() {
    //sin destruir
    if (!destroyed) {
        
     /**********DIBUJAR LA SOMBRA*************/
        glPushMatrix();
        activarModoSombra();
        
        glTranslatef(x, y, z);
        
        glmDraw(model, GLM_SMOOTH);

        desactivarModoSombra();
        glPopMatrix();


        /**********DIBUJAR EL OBJETIVO*************/
        glPushMatrix();
        if (selection_mode) {
            glPushName(id);
        }

        glTranslatef(x, y, z);
        glScalef(escala, escala, escala);
        glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient);
        glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_difuso);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_especular);
        glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emision);
        glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, material_brillo);

        //cargar modelo del objetivo
        glmDraw(model, GLM_SMOOTH);


        glPopMatrix();

        if (selection_mode) {
            glPopName();
        }


    } else { //destruido
        //el dibujo de la explosion debe durar 
        //la cantidad de frames especificada por
        //EXPLOSION_LIMIT
        if (explosion_counter < EXPLOSION_LIMIT) {

            glPushMatrix();

            GLfloat mat_amb[] = {0.15, 0.15, 0.15};
            GLfloat mat_esp[] = {0.0, 0.0, 0.0};
            GLfloat mat_diff[] = {1.0, 0.5, 0.0};
            GLfloat mat_emiss[] = {1.0, 0.5, 0.0};
            GLfloat mat_shin[] = {128};

            //configuracion de los materiales de la explosion
            glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, mat_amb);
            glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, mat_diff);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, mat_esp);
            glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, mat_emiss);
            glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, mat_shin);
            glTranslatef(x, y, z);


            //dibujar explosion como esfera naranja cuyo radio
            //aumenta en el tiempo.
            glutSolidSphere(2 + (explosion_counter) / 15, 30, 30);

            GLfloat position[] = {x, y, z + 2.0, 1.0};
            GLfloat ambiental[] = {0.15, 0.15, 0.15, 1.0};
            GLfloat especular[] = {1.0, 0.5, 1.0, 1.0};
            GLfloat diffuse_explosion[] = {1.0, 0.5, 0.0, 1.0};


            //configurar luces de la explosion con tonalidad naranja
            glLightfv(luz_explosion, GL_POSITION, position);
            glLightfv(luz_explosion, GL_DIFFUSE, diffuse_explosion);
            glLightfv(luz_explosion, GL_AMBIENT, ambiental);
            glLightfv(luz_explosion, GL_SPECULAR, especular);
            glLightf(luz_explosion, GL_CONSTANT_ATTENUATION, 1.0);
            glLightf(luz_explosion, GL_LINEAR_ATTENUATION, 0.0);
            glLightf(luz_explosion, GL_QUADRATIC_ATTENUATION, 0.0);
            glEnable(luz_explosion);
            explosion_counter++;
            glPopMatrix();

        } else {

            if (!recovered) {
                //desabilitar la luz de la explosion
                glDisable(luz_explosion);

                //verificar si se reutilizo la luz del laser que impacto
                //restablecer los valores de luz del laser y la luz
                //de explosion por defecto del objetivo
                if (luz_explosion != GL_LIGHT4) {

                    afectado->restaurarLuz();
                    luz_explosion = GL_LIGHT4;
                }

                recovered = true;


            }


        }


    }

}

/**
 * metodo que permite verificar las colisiones del objetivo*
 * con los lasers y con la nave
 *
 * @param skip : El frameskip actual.
 */
void Objetivo::idle(int skip) {
    int s = skip * 2; /* Default: s = 1 */
    z += s;

    if (!destroyed) {
        if (z < 0) {
            //           
            list<Laser>::iterator it = nave->disparos.begin();

            while (it != nave->disparos.end()) {
                if ((*it).activado()) {
                    float xd = x - (*it).getX();
                    float yd = y - (*it).getY();
                    float zd = z - (*it).getZ();
                    if (RADIO > distancia(xd, yd, zd)) {
                        /* Colision laser vs Objetivo*/
                        alSourcePlay(source[0]);
                        nave->addScore(1);
                        destroyed = true;
                        luz_explosion = (*it).getLuz();
                        (*it).desactivar(false);
                        afectado = &(*it);


                    }
                }

                it++;
            }


        }
        if (0 <= z && z < s && !nave->getImpactada()) {
            float xd = abs(x - nave->getX());
            float yd = abs(y - nave->getY());
            float vl = distancia(xd, yd, 0.0);

            if (vl < RADIO) { //colision nave contra objetivo
                nave->addScore(-3);
                nave->setImpactada();
                destroyed = true;
                luz_explosion = GL_LIGHT4;
                alSourcePlay(source[0]);
            }
            nave->avanzarObjetivo();
        }
    }

    if (z >= ZKILL) { //si la nave llega al principio de la pantalla reposicionar
        randomPos();
    }
}

/**
 * Este metodo permite colocar el objetivo en una posicion psudoaleatoria
 * en el plano XY.
 */
void Objetivo::randomPos() {
    x = rand() % (ancho_pantalla * 2) - ancho_pantalla; // -ancho < rx < ancho
    y = rand() % (alto_pantalla * 2) - alto_pantalla; // -alto < ry < alto
    z = ZSPAWN;
    recovered = false;
    destroyed = false;
    explosion_counter = 0;
}

//funcion auxiliar para calcular la distancia ente dos puntos

float Objetivo::distancia(float x, float y, float z) {
    return sqrt(x * x + y * y + z * z);
}

/**
 * Este metodo que permite mover el objetivo una cantidad de unidades 
 * hacia el frente.
 * 
 * @param units : La cantidad de unidades a mover
 */ 
void Objetivo::empujar(int units) {
    z += units;
}

